﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sharp3D.Math.Core;
using System.Diagnostics;

namespace GLWrap.SceneObjects.Line
{
    public class BaseLine: SceneObjectsGroup
    {
        protected Vector3F _pointA;
        protected Vector3F _pointB;
        protected float    _weight;
        private IDictionary<GlSceneManager, bool> _dirtyForSceneManager;

        #region Public Properties
        public virtual Vector3F PointA 
        { 
            get { return _pointA; } 
            set 
            { 
                _pointA = value;
                MarkAsDirty();
            } 
        }

        public virtual Vector3F PointB 
        { 
            get { return _pointB; } 
            set 
            { 
                _pointB = value;
                MarkAsDirty();
            } 
        }

        public virtual float Weight 
        { 
            get { return _weight; } 
            set { _weight = value; } 
        }
        #endregion

        public BaseLine()
        {
            //TODO: constant?
            _weight = 0.05f;
            _dirtyForSceneManager = new Dictionary<GlSceneManager, bool>();
        }

        #region Internal
        protected void MarkAsDirty()
        {
            SetDirty(true);
        }

        protected void MarkAsClean()
        {
            SetDirty(false);
        }

        private void SetDirty(bool dirty)
        {
            foreach (var key in _dirtyForSceneManager.Keys.ToArray())
                _dirtyForSceneManager[key] = dirty;
        }

        protected bool IsDirtyForSceneManager(GlSceneManager sceneManager)
        {
            CheckSceneManager(sceneManager);
            return _dirtyForSceneManager[sceneManager];
        }

        protected void SetDirtyForSceneManager(GlSceneManager sceneManager,bool value)
        {
            CheckSceneManager(sceneManager);
            _dirtyForSceneManager[sceneManager] = value;
        }

        private void CheckSceneManager(GlSceneManager sceneManager)
        {
            if (!_dirtyForSceneManager.ContainsKey(sceneManager))
                throw new ArgumentException("Object is not attached to scene, cannot draw");
        }

        private void RecalculateLineForSceneManager(GlSceneManager sceneManager)
        {
            InternalRecalculateLineForSceneManager(sceneManager);
            SetDirtyForSceneManager(sceneManager, false);
        }
        #endregion

        #region SceneObjectsGroup overrides
        protected sealed override void GroupWillBeDrawn(GlSceneManager sceneManager)
        {
            if (IsDirtyForSceneManager(sceneManager))
                RecalculateLineForSceneManager(sceneManager);

            InternalLineWillBeDrawn(sceneManager);
        }

        protected sealed override void GroupAttachedToScene(GlSceneManager sceneManager)
        {
            _dirtyForSceneManager[sceneManager] = true;
            InternalLineAttachedToScene(sceneManager);
        }

        protected sealed override void GroupDetachedFromScene(GlSceneManager sceneManager)
        {
            _dirtyForSceneManager.Remove(sceneManager);
            InternalLineDetachedFromScene(sceneManager);
        }
        #endregion

        protected virtual void InternalRecalculateLineForSceneManager(GlSceneManager sceneManager) {}
        protected virtual void InternalLineWillBeDrawn(GlSceneManager sceneManager) { }
        protected virtual void InternalLineAttachedToScene(GlSceneManager sceneManager) {}
        protected virtual void InternalLineDetachedFromScene(GlSceneManager sceneManager) {}
    }
}
